/*
 * Copyright (C) 2005 - 2013 MaNGOS <http://www.getmangos.com/>
 *
 * Copyright (C) 2008 - 2013 Trinity <http://www.trinitycore.org/>
 *
 * Copyright (C) 2006 - 2013 ScriptDev2 <http://www.scriptdev2.com/>
 *
 * Copyright (C) 2010 - 2013 ProjectSkyfire <http://www.projectskyfire.org/>
 *
 * Copyright (C) 2011 - 2013 ArkCORE <http://www.arkania.net/>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

/* ScriptData
 SDName: Eversong_Woods
 SD%Complete: 100
 SDComment: Quest support: 8483, 8488, 8490, 9686
 SDCategory: Eversong Woods
 EndScriptData */

/* ContentData
 npc_prospector_anvilward
 npc_apprentice_mirveda
 npc_infused_crystal
 npc_kelerun_bloodmourn
 go_harbinger_second_trial
 EndContentData */

#include "ScriptPCH.h"
#include "ScriptedEscortAI.h"

/*######
 ## npc_prospector_anvilward
 ######*/

#define GOSSIP_HELLO    "I need a moment of your time, sir."
#define GOSSIP_SELECT   "Why... yes, of course. I've something to show you right inside this building, Mr. Anvilward."

enum eProspectorAnvilward {
    SAY_ANVIL1 = -1000209, SAY_ANVIL2 = -1000210, QUEST_THE_DWARVEN_SPY = 8483,
};

class npc_prospector_anvilward: public CreatureScript {
public:
    npc_prospector_anvilward() :
            CreatureScript("npc_prospector_anvilward") {
    }

    bool OnGossipSelect(Player* pPlayer, Creature* pCreature,
            uint32 /*uiSender*/, uint32 uiAction) {
        pPlayer->PlayerTalkClass->ClearMenus();
        switch (uiAction) {
        case GOSSIP_ACTION_INFO_DEF + 1:
            pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SELECT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2);
            pPlayer->SEND_GOSSIP_MENU(8240, pCreature->GetGUID());
            break;
        case GOSSIP_ACTION_INFO_DEF + 2:
            pPlayer->CLOSE_GOSSIP_MENU();
            if (npc_escortAI* pEscortAI = CAST_AI(npc_prospector_anvilward::npc_prospector_anvilwardAI, pCreature->AI()))
                pEscortAI->Start(true, false, pPlayer->GetGUID());
            break;
        }
        return true;
    }

    bool OnGossipHello(Player* pPlayer, Creature* pCreature) {
        if (pPlayer->GetQuestStatus(QUEST_THE_DWARVEN_SPY)
                == QUEST_STATUS_INCOMPLETE)
            pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);

        pPlayer->SEND_GOSSIP_MENU(8239, pCreature->GetGUID());
        return true;
    }

    CreatureAI* GetAI(Creature* pCreature) const {
        return new npc_prospector_anvilwardAI(pCreature);
    }

    struct npc_prospector_anvilwardAI: public npc_escortAI {
        // CreatureAI functions
        npc_prospector_anvilwardAI(Creature *c) :
                npc_escortAI(c) {
        }

        // Pure Virtual Functions
        void WaypointReached(uint32 i) {
            Player* pPlayer = GetPlayerForEscort();

            if (!pPlayer)
                return;

            switch (i) {
            case 0:
                DoScriptText(SAY_ANVIL1, me, pPlayer);
                break;
            case 5:
                DoScriptText(SAY_ANVIL2, me, pPlayer);
                break;
            case 6:
                me->setFaction(24);
                break;
            }
        }

        void Reset() {
            me->RestoreFaction();
        }

        void JustDied(Unit* /*killer*/) {
            me->RestoreFaction();
        }
    };
};

/*######
 ## Quest 9686 Second Trial
 ######*/

enum SeconTrial {
    QUEST_SECOND_TRIAL = 9686, OFFSET_NEXT_ATTACK = 750,
};

enum eSpells {
    SPELL_FLASH_OF_LIGHT = 19939,
    SPELL_SEAL_OF_JUSTICE = 20164,
    SPELL_JUDGEMENT_OF_LIGHT = 20271,
    SPELL_SEAL_OF_COMMAND = 20375,
};

enum eNpc {
    MASTER_KELERUN_BLOODMOURN = 17807,
    CHAMPION_BLOODWRATH = 17809,
    CHAMPION_LIGHTREND = 17810,
    CHAMPION_SWIFTBLADE = 17811,
    CHAMPION_SUNSTRIKER = 17812,
};

enum eFaction {
    FACTION_HOSTILE = 45, FACTION_FRIENDLY = 7,
};

enum eSays {
    TEXT_SECOND_TRIAL_1 = -1000637,
    TEXT_SECOND_TRIAL_2 = -1000638,
    TEXT_SECOND_TRIAL_3 = -1000639,
    TEXT_SECOND_TRIAL_4 = -1000640,
};

struct Locations {
    float x, y, z, o;
};

static Locations SpawnPosition[] = { { 5.3f, -11.8f, 0.361f, 4.2f }, { 11.2f,
        -29.17f, 0.361f, 2.7f }, { -5.7f, -34.85f, 0.361f, 1.09f }, { -11.9f,
        -18, 0.361f, 5.87f } };

static uint32 PaladinEntry[] = { CHAMPION_BLOODWRATH, CHAMPION_LIGHTREND,
        CHAMPION_SWIFTBLADE, CHAMPION_SUNSTRIKER };

/*######
 ## npc_second_trial_paladin
 ######*/

class npc_second_trial_paladin: public CreatureScript {
public:
    npc_second_trial_paladin() :
            CreatureScript("npc_second_trial_paladin") {
    }

    CreatureAI* GetAI(Creature* pCreature) const {
        return new npc_secondTrialAI(pCreature);
    }

    struct npc_secondTrialAI: public ScriptedAI {
        npc_secondTrialAI(Creature* pCreature) :
                ScriptedAI(pCreature) {
        }

        uint32 timer;
        uint8 questPhase;
        uint64 summonerGuid;

        bool spellFlashLight;
        bool spellJustice;
        bool spellJudLight;
        bool spellCommand;

        uint32 timerFlashLight;
        uint32 timerJustice;
        uint32 timerJudLight;
        uint32 timerCommand;

        void Reset() {
            timer = 2000;
            questPhase = 0;
            summonerGuid = 0;

            me->SetUInt32Value(UNIT_FIELD_BYTES_1, UNIT_STAND_STATE_KNEEL);
            me->setFaction(FACTION_FRIENDLY);

            spellFlashLight = false;
            spellJustice = false;
            spellJudLight = false;
            spellCommand = false;

            switch (me->GetEntry()) {
            case CHAMPION_BLOODWRATH:
                spellFlashLight = true;
                timerFlashLight = 3225;
                break;
            case CHAMPION_LIGHTREND:
                spellJustice = true;
                timerJustice = 500;
                break;
            case CHAMPION_SWIFTBLADE:
                spellJudLight = false; // Misses Script Effect // http://www.wowhead.com/?spell=20271
                timerJudLight = 500;
                break;
            case CHAMPION_SUNSTRIKER:
                spellFlashLight = true;
                spellJudLight = false; // Misses Script Effect // http://www.wowhead.com/?spell=20271
                spellCommand = false; // Misses Dummy // http://www.wowhead.com/?spell=20375
                timerFlashLight = 3225;
                timerJudLight = 500;
                timerCommand = 1500;
                break;
            }
        }

        void EnterCombat(Unit * /*who*/) {
        }

        void UpdateAI(const uint32 diff) {
            if (questPhase == 1) {
                if (timer <= diff) {
                    me->SetUInt32Value(UNIT_FIELD_BYTES_1,
                            UNIT_STAND_STATE_STAND);
                    me->setFaction(FACTION_HOSTILE);
                    questPhase = 0;

                    if (Unit *pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) {
                        me->AddThreat(pTarget, 5000000.0f);
                        AttackStart(pTarget);
                    }
                } else
                    timer -= diff;
            }

            if (!UpdateVictim())
                return;

            // healer
            if (spellFlashLight && HealthBelowPct(70)) {
                if (timerFlashLight <= diff) {
                    DoCast(me, SPELL_FLASH_OF_LIGHT);
                    timerFlashLight = 3225 + rand() % 3225;
                } else
                    timerFlashLight -= diff;
            }

            if (spellJustice) {
                if (timerJustice <= diff) {
                    DoCast(me, SPELL_SEAL_OF_JUSTICE);
                    timerJustice = 10000 + rand() % 10000;
                } else
                    timerJustice -= diff;
            }

            if (spellJudLight) {
                if (timerJudLight <= diff) {
                    DoCast(me, SPELL_JUDGEMENT_OF_LIGHT);
                    timerJudLight = 10000 + rand() % 10000;
                } else
                    timerJudLight -= diff;
            }

            if (spellCommand) {
                if (timerCommand <= diff) {
                    DoCast(me, SPELL_SEAL_OF_COMMAND);
                    timerCommand = 20000 + rand() % 20000;
                } else
                    timerCommand -= diff;
            }

            DoMeleeAttackIfReady();
        }

        void Activate(uint64 summonerguid) {
            questPhase = 1;
            summonerGuid = summonerguid;
        }

        void KilledUnit(Unit* Killed) {
            if (Killed->GetTypeId() == TYPEID_PLAYER)
                if (CAST_PLR(Killed)->GetQuestStatus(QUEST_SECOND_TRIAL)
                        == QUEST_STATUS_INCOMPLETE)
                    CAST_PLR(Killed)->FailQuest(QUEST_SECOND_TRIAL);
        }

        void JustDied(Unit* Killer);
    };
};

/*######
 ## npc_second_trial_controller
 ######*/

class npc_second_trial_controller: public CreatureScript {
public:
    npc_second_trial_controller() :
            CreatureScript("npc_second_trial_controller") {
    }

    bool OnQuestAccept(Player* /*pPlayer*/, Creature* pCreature,
            Quest const *quest) {
        // One Player exclusive quest, wait for user go activation
        if (quest->GetQuestId() == QUEST_SECOND_TRIAL)
            CAST_AI(npc_second_trial_controller::master_kelerun_bloodmournAI, pCreature->AI())->questPhase =
                    1;

        return true;
    }

    bool OnGossipHello(Player* pPlayer, Creature* pCreature) {
        // quest only available if not already started
        // Quest_template flag is set to : QUEST_FLAGS_EVENT
        // Escort quests or any other event-driven quests. If player in party, all players that can accept this quest will receive confirmation box to accept quest.
        // !not sure if this really works!

        if (CAST_AI(npc_second_trial_controller::master_kelerun_bloodmournAI, pCreature->AI())->questPhase
                == 0) {
            pPlayer->PrepareQuestMenu(pCreature->GetGUID());
            pPlayer->SendPreparedQuest(pCreature->GetGUID());
        }

        pPlayer->SEND_GOSSIP_MENU(pCreature->GetEntry(), pCreature->GetGUID());
        return true;
    }

    CreatureAI* GetAI(Creature* pCreature) const {
        return new master_kelerun_bloodmournAI(pCreature);
    }

    struct master_kelerun_bloodmournAI: public ScriptedAI {
        master_kelerun_bloodmournAI(Creature *c) :
                ScriptedAI(c) {
        }

        uint8 questPhase;
        uint8 paladinPhase;
        uint32 timer;

        uint64 paladinGuid[4];

        void Reset() {
            questPhase = 0;
            timer = 60000;
            paladinPhase = 0;
            for (uint8 i = 0; i < 4; ++i)
                paladinGuid[i] = 0;
        }

        void EnterCombat(Unit * /*who*/) {
        }

        void UpdateAI(const uint32 diff) {
            // Quest accepted but object not activated, object despawned (if in sync 1 minute!)
            if (questPhase == 1) {
                if (timer <= diff)
                    Reset();
                else
                    timer -= diff;
            }
            // fight the 4 paladin mobs phase
            else if (questPhase == 2) {
                if (timer <= diff) {
                    if (Creature* paladinSpawn = Unit::GetCreature((*me), paladinGuid[paladinPhase])) {
                        CAST_AI(npc_second_trial_paladin::npc_secondTrialAI, paladinSpawn->AI())->Activate(
                                me->GetGUID());

                        switch (paladinPhase) {
                        case 0:
                            DoScriptText(TEXT_SECOND_TRIAL_1, me);
                            break;
                        case 1:
                            DoScriptText(TEXT_SECOND_TRIAL_2, me);
                            break;
                        case 2:
                            DoScriptText(TEXT_SECOND_TRIAL_3, me);
                            break;
                        case 3:
                            DoScriptText(TEXT_SECOND_TRIAL_4, me);
                            break;
                        }
                    } else
                        Reset();

                    questPhase = 4;
                    timer = OFFSET_NEXT_ATTACK;
                } else
                    timer -= diff;
            }

            if (!UpdateVictim())
                return;

            DoMeleeAttackIfReady();
        }

        void StartEvent() {
            if (questPhase == 1) { // no player check, quest can be finished as group, so no complex PlayerGUID/group search code
                for (uint8 i = 0; i < 4; ++i)
                    if (Creature *pSummoned = DoSpawnCreature(PaladinEntry[i], SpawnPosition[i].x, SpawnPosition[i].y, SpawnPosition[i].z, SpawnPosition[i].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 180000))
                        paladinGuid[i] = pSummoned->GetGUID();

                timer = OFFSET_NEXT_ATTACK;
                questPhase = 2;
            }
        }

        void SecondTrialKill() {
            if (questPhase > 0) {
                ++paladinPhase;

                if (paladinPhase < 4)
                    questPhase = 2;
                else
                    Reset(); // Quest Complete, QuestComplete handler is
            }
        }

        void SummonedCreatureDespawn(Creature* /*c*/) {
        }
    };
};

void npc_second_trial_paladin::npc_secondTrialAI::JustDied(Unit* Killer) {
    if (Killer->GetTypeId() == TYPEID_PLAYER) {
        if (Creature *pSummoner = Unit::GetCreature((*me), summonerGuid))
            CAST_AI(npc_second_trial_controller::master_kelerun_bloodmournAI, pSummoner->AI())->SecondTrialKill();

        // last kill quest complete for group
        if (me->GetEntry() == CHAMPION_SUNSTRIKER) {
            if (Killer->GetTypeId() == TYPEID_PLAYER)
                Killer->ToPlayer()->GroupEventHappens(QUEST_SECOND_TRIAL,
                        Killer);
        }
    }
}

/*######
 ## go_second_trial
 ######*/
class go_second_trial: public GameObjectScript {
public:
    go_second_trial() :
            GameObjectScript("go_second_trial") {
    }

    bool OnGossipHello(Player* /*pPlayer*/, GameObject* pGO) {
        // find spawn :: master_kelerun_bloodmourn
        if (Creature *pCreature = pGO->FindNearestCreature(MASTER_KELERUN_BLOODMOURN, 30.0f))
            CAST_AI(npc_second_trial_controller::master_kelerun_bloodmournAI, pCreature->AI())->StartEvent();

        return true;
    }
};

/*######
 ## npc_apprentice_mirveda
 ######*/

#define QUEST_UNEXPECTED_RESULT 8488
#define MOB_GHARZUL     15958
#define MOB_ANGERSHADE  15656

class npc_apprentice_mirveda: public CreatureScript {
public:
    npc_apprentice_mirveda() :
            CreatureScript("npc_apprentice_mirveda") {
    }

    bool OnQuestAccept(Player* pPlayer, Creature* pCreature,
            Quest const* quest) {
        if (quest->GetQuestId() == QUEST_UNEXPECTED_RESULT) {
            CAST_AI(npc_apprentice_mirveda::npc_apprentice_mirvedaAI, pCreature->AI())->Summon =
                    true;
            CAST_AI(npc_apprentice_mirveda::npc_apprentice_mirvedaAI, pCreature->AI())->PlayerGUID =
                    pPlayer->GetGUID();
        }
        return true;
    }

    CreatureAI* GetAI(Creature* pCreature) const {
        return new npc_apprentice_mirvedaAI(pCreature);
    }

    struct npc_apprentice_mirvedaAI: public ScriptedAI {
        npc_apprentice_mirvedaAI(Creature* c) :
                ScriptedAI(c), Summons(me) {
        }

        uint32 KillCount;
        uint64 PlayerGUID;
        bool Summon;
        SummonList Summons;

        void Reset() {
            KillCount = 0;
            PlayerGUID = 0;
            Summons.DespawnAll();
            Summon = false;
        }

        void EnterCombat(Unit* /*who*/) {
        }

        void JustSummoned(Creature *summoned) {
            summoned->AI()->AttackStart(me);
            Summons.Summon(summoned);
        }

        void SummonedCreatureDespawn(Creature* summoned) {
            Summons.Despawn(summoned);
            ++KillCount;
        }

        void JustDied(Unit* /*killer*/) {
            if (PlayerGUID)
                if (Player* pPlayer = Unit::GetPlayer(*me, PlayerGUID))
                    CAST_PLR(pPlayer)->FailQuest(QUEST_UNEXPECTED_RESULT);
        }

        void UpdateAI(const uint32 /*diff*/) {
            if (KillCount >= 3 && PlayerGUID)
                if (Player* pPlayer = Unit::GetPlayer(*me, PlayerGUID))
                    CAST_PLR(pPlayer)->CompleteQuest(QUEST_UNEXPECTED_RESULT);

            if (Summon) {
                me->SummonCreature(MOB_GHARZUL, 8745, -7134.32f, 35.22f, 0,
                        TEMPSUMMON_CORPSE_DESPAWN, 4000);
                me->SummonCreature(MOB_ANGERSHADE, 8745, -7134.32f, 35.22f, 0,
                        TEMPSUMMON_CORPSE_DESPAWN, 4000);
                me->SummonCreature(MOB_ANGERSHADE, 8745, -7134.32f, 35.22f, 0,
                        TEMPSUMMON_CORPSE_DESPAWN, 4000);
                Summon = false;
            }
        }
    };
};

/*######
 ## npc_infused_crystal
 ######*/

#define MOB_ENRAGED_WRAITH  17086
#define EMOTE   -1000283
#define QUEST_POWERING_OUR_DEFENSES 8490

struct Location {
    float x, y, z;
};

static Location SpawnLocations[] = { { 8270.68f, -7188.53f, 139.619f }, {
        8284.27f, -7187.78f, 139.603f }, { 8297.43f, -7193.53f, 139.603f }, {
        8303.5f, -7201.96f, 139.577f }, { 8273.22f, -7241.82f, 139.382f }, {
        8254.89f, -7222.12f, 139.603f }, { 8278.51f, -7242.13f, 139.162f }, {
        8267.97f, -7239.17f, 139.517f } };

class npc_infused_crystal: public CreatureScript {
public:
    npc_infused_crystal() :
            CreatureScript("npc_infused_crystal") {
    }

    CreatureAI* GetAI(Creature* pCreature) const {
        return new npc_infused_crystalAI(pCreature);
    }

    struct npc_infused_crystalAI: public Scripted_NoMovementAI {
        npc_infused_crystalAI(Creature* c) :
                Scripted_NoMovementAI(c) {
        }

        uint32 EndTimer;
        uint32 WaveTimer;
        bool Completed;
        bool Progress;
        uint64 PlayerGUID;

        void Reset() {
            EndTimer = 0;
            Completed = false;
            Progress = false;
            PlayerGUID = 0;
            WaveTimer = 0;
        }

        void MoveInLineOfSight(Unit* who) {
            if (!Progress && who->GetTypeId() == TYPEID_PLAYER
                    && me->IsWithinDistInMap(who, 10.0f)) {
                if (CAST_PLR(who)->GetQuestStatus(QUEST_POWERING_OUR_DEFENSES)
                        == QUEST_STATUS_INCOMPLETE) {
                    PlayerGUID = who->GetGUID();
                    WaveTimer = 1000;
                    EndTimer = 60000;
                    Progress = true;
                }
            }
        }

        void JustSummoned(Creature *summoned) {
            summoned->AI()->AttackStart(me);
        }

        void JustDied(Unit* /*killer*/) {
            if (PlayerGUID && !Completed)
                if (Player* pPlayer = Unit::GetPlayer(*me, PlayerGUID))
                    CAST_PLR(pPlayer)->FailQuest(QUEST_POWERING_OUR_DEFENSES);
        }

        void UpdateAI(const uint32 diff) {
            if (EndTimer < diff && Progress) {
                DoScriptText(EMOTE, me);
                Completed = true;
                if (PlayerGUID)
                    if (Player* pPlayer = Unit::GetPlayer(*me, PlayerGUID))
                        CAST_PLR(pPlayer)->CompleteQuest(
                                QUEST_POWERING_OUR_DEFENSES);

                me->DealDamage(me, me->GetHealth(), NULL, DIRECT_DAMAGE,
                        SPELL_SCHOOL_MASK_NORMAL, NULL, false);
                me->RemoveCorpse();
            } else
                EndTimer -= diff;

            if (WaveTimer < diff && !Completed && Progress) {
                uint32 ran1 = rand() % 8;
                uint32 ran2 = rand() % 8;
                uint32 ran3 = rand() % 8;
                me->SummonCreature(MOB_ENRAGED_WRAITH, SpawnLocations[ran1].x,
                        SpawnLocations[ran1].y, SpawnLocations[ran1].z, 0,
                        TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 10000);
                me->SummonCreature(MOB_ENRAGED_WRAITH, SpawnLocations[ran2].x,
                        SpawnLocations[ran2].y, SpawnLocations[ran2].z, 0,
                        TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 10000);
                me->SummonCreature(MOB_ENRAGED_WRAITH, SpawnLocations[ran3].x,
                        SpawnLocations[ran3].y, SpawnLocations[ran3].z, 0,
                        TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 10000);
                WaveTimer = 30000;
            } else
                WaveTimer -= diff;
        }
    };
};

void AddSC_eversong_woods() {
    new npc_prospector_anvilward();
    new npc_second_trial_controller();
    new npc_second_trial_paladin();
    new go_second_trial();
    new npc_apprentice_mirveda();
    new npc_infused_crystal();
}
